from functools import wraps
from flask import current_app  # 关键：添加current_app导入
from flask import Flask, render_template, request, jsonify, session, redirect, url_for, flash, g
import html 
from flask_wtf.csrf import generate_csrf
from judge.judge import Judge
from flask_login import LoginManager, login_required, current_user, login_user, logout_user
from werkzeug.security import generate_password_hash, check_password_hash
from .models import db, User, Problem, Submission, Discussion, Comment, user_solved, Favorite, ClockInRecord, ForumPost, ForumComment, PostLike, Notification  
import json
import traceback  # 导入错误追踪模块（解决第二个错误）
from werkzeug.utils import secure_filename
import os
from datetime import datetime, timedelta, date
import calendar
import sys
from pathlib import Path
from flask_wtf.csrf import CSRFProtect
import pytz
import uuid
from sqlalchemy import func
from sqlalchemy import or_, desc
from .forms import ProblemCreateForm  # 导入表单类
from pathlib import Path

# 获取项目根目录
project_root = Path(__file__).resolve().parent.parent
sys.path.append(str(project_root))

from judge.judge import Judge

# 初始化时区（北京时间）
tz = pytz.timezone('Asia/Shanghai')

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///judge.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.secret_key = app.config['SECRET_KEY']

# 初始化扩展
db.init_app(app)
judge = Judge()
csrf = CSRFProtect(app)

# 初始化LoginManager并绑定到应用
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'
login_manager.login_message = ''

# 用户加载回调
@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

TESTCASES_ROOT = 'problem_testcases'
os.makedirs(TESTCASES_ROOT, exist_ok=True)

# 配置上传目录和允许的文件类型（使用 app.config 而非 current_app）
UPLOAD_FOLDER = 'static/uploads/avatars'
app.config['UPLOAD_FOLDER'] = os.path.join(app.root_path, 'static', 'uploads', 'avatars')
app.config['MAX_CONTENT_LENGTH'] = 2 * 1024 * 1024  # 2MB限制

# 配置模板文件存储路径（建议放在项目根目录的 cpp_templates 下）
CPP_TEMPLATE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../code_templates')
# 确保目录存在
Path(CPP_TEMPLATE_DIR).mkdir(parents=True, exist_ok=True)

# 确保上传目录存在
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)

# 验证文件类型
def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in {'png', 'jpg', 'jpeg', 'gif'}

# 个人设置路由
@app.route('/settings', methods=['GET', 'POST'])
@login_required
def user_settings():
    user = current_user  # 获取当前登录用户

    if request.method == 'POST':
        # 1. 处理头像上传
        avatar_updated = False
        if 'avatar' in request.files:
            file = request.files['avatar']
            if file.filename:  # 确保有文件被选择
                # 验证文件类型
                allowed_extensions = {'png', 'jpg', 'jpeg'}
                if '.' not in file.filename or \
                   file.filename.rsplit('.', 1)[1].lower() not in allowed_extensions:
                    flash('仅支持PNG、JPG、JPEG格式的图片', 'danger')
                    return redirect(url_for('user_settings'))
                
                # 验证文件大小（≤2MB）
                if file.content_length > 2 * 1024 * 1024:
                    flash('图片大小不能超过2MB', 'danger')
                    return redirect(url_for('user_settings'))
                
                # 生成唯一文件名（避免覆盖）：用户ID+时间戳+原文件名
                from datetime import datetime
                timestamp = datetime.now().strftime('%Y%m%d%H%M%S')
                filename = f"{user.id}_{timestamp}_{secure_filename(file.filename)}"
                save_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
                
                # 保存文件并更新用户头像
                try:
                    file.save(save_path)
                    user.avatar = filename  # 更新用户头像字段
                    avatar_updated = True
                except Exception as e:
                    flash(f'头像上传失败：{str(e)}', 'danger')
                    return redirect(url_for('user_settings'))

        # 2. 处理邮箱更新
        email_updated = False
        new_email = request.form.get('email', '').strip()
        if new_email and new_email != user.email:  # 邮箱有变化时才处理
            existing_user = User.query.filter_by(email=new_email).first()
            if existing_user:
                flash('邮箱已被占用，请更换', 'danger')
                return redirect(url_for('user_settings'))
            user.email = new_email
            email_updated = True

        # 3. 处理密码更新
        password_changed = False
        new_password = request.form.get('password', '').strip()
        new_password_confirm = request.form.get('confirm_password', '').strip()
        
        if new_password:  # 新密码非空时才处理
            # 验证确认密码
            if not new_password_confirm:
                flash('确认密码不能为空', 'danger')
                return redirect(url_for('user_settings'))
            if new_password != new_password_confirm:
                flash('两次密码输入不一致', 'danger')
                return redirect(url_for('user_settings'))
            # 验证与原密码不同
            if check_password_hash(user.password_hash, new_password):
                flash('新密码不能与原密码相同', 'danger')
                return redirect(url_for('user_settings'))
            
            # 更新密码
            user.password_hash = generate_password_hash(new_password)
            password_changed = True

        # 4. 提交所有修改到数据库
        try:
            db.session.commit()
            # 构建成功消息
            success_msg = []
            if avatar_updated:
                success_msg.append('头像更新成功')
            if email_updated:
                success_msg.append('邮箱更新成功')
            if password_changed:
                success_msg.append(
                    json.dumps({
                        'message': '密码已修改，{count}秒后自动跳转至登录页',
                        'password_changed': True,
                        'count': 3
                    })
                )
            # 若没有具体修改项，显示默认成功消息
            if not success_msg:
                flash('设置保存成功', 'success')
            else:
                for msg in success_msg:
                    flash(msg, 'success')
        except Exception as e:
            db.session.rollback()
            flash(f'保存失败：{str(e)}', 'danger')
            return redirect(url_for('user_settings'))

        # 密码修改后需要跳转登录页（其他修改跳转回设置页）
        if password_changed:
            return redirect(url_for('login'))
        else:
            return redirect(url_for('user_settings'))

    # GET请求：渲染设置页面（仅传递user变量，不传递form）
    return render_template('settings.html', user=user)

# ------------------------------
# 核心功能：自动创建超级管理员
# ------------------------------
def create_default_superuser():
    """应用首次启动时自动创建root超级管理员（密码123）"""
    # 检查是否已存在超级管理员或root用户
    existing_super = User.query.filter_by(is_superuser=True).first()
    existing_root = User.query.filter_by(username='root').first()
    
    if existing_super or existing_root:
        print("📌 已存在超级管理员，无需重复创建")
        return
    
    try:
        # 创建root用户（密码自动加密）
        admin = User(
            username='root',
            password_hash=generate_password_hash('123'),  # 密码123
            is_superuser=True  # 标记为超级管理员
        )
        db.session.add(admin)
        db.session.commit()
        print("✅ 超级管理员创建成功：用户名=root，密码=123（请首次登录后修改）")
    except Exception as e:
        db.session.rollback()
        print(f"❌ 超级管理员创建失败：{str(e)}")

@app.route('/admin/dashboard')
@login_required
def admin_dashboard():
    """root 专属管理主页，包含题库、用户、论坛管理入口"""
    # 权限校验：仅 root 可访问
    if current_user.username != 'root':
        flash('无权限访问管理员页面', 'danger')
        return redirect(url_for('problems'))  # 非 root 跳转至普通页面
    
    return render_template('admin/dashboard.html', user=current_user)

@app.route('/admin/manage_problems', methods=['GET'])
@login_required
def admin_manage_problems():
    """
    题目列表页：支持搜索、难度筛选、状态筛选、分页
    """
    # 1. 解析请求参数
    search = request.args.get('search', '').strip()
    difficulty = request.args.get('difficulty', '')
    status = request.args.get('status', '')  # complete/incomplete
    page = request.args.get('page', 1, type=int)
    per_page = 10  # 每页显示数量

    # 2. 构建查询条件
    query = Problem.query

    # 搜索：支持标题模糊搜索或ID精确搜索
    if search:
        query = query.filter(
            or_(Problem.title.ilike(f'%{search}%'), Problem.id == search)
        )

    # 难度筛选（easy/medium/hard）
    if difficulty:
        query = query.filter(Problem.difficulty == difficulty)

    # 3. 分页查询（按ID倒序）
    pagination = query.order_by(Problem.id.desc()).paginate(
        page=page, 
        per_page=per_page, 
        error_out=False
    )
    problems = pagination.items

    # 4. 渲染模板（传递筛选参数和分页对象）
    return render_template(
        'admin/manage_problems.html', user=current_user,
        problems=problems, 
        pagination=pagination, 
        search=search, 
        difficulty=difficulty, 
        status=status
    )


@app.route('/admin/problems/create', methods=['GET', 'POST'])
@login_required  # 要求登录
def create_problem():
    # 权限校验：非超级用户禁止访问
    if not current_user.is_superuser:
        abort(403)
    
    form = ProblemCreateForm()
    if form.validate_on_submit():
        try:
            # 1. 解析JSON字段（确保格式正确）
            # 增加空值判断，避免解析空字符串报错
            initial_codes = json.loads(form.initial_codes.data) if form.initial_codes.data.strip() else {}
            function_signature = json.loads(form.function_signature.data) if form.function_signature.data.strip() else {}
            
            # 2. 处理测试用例目录（确保目录存在）
            testcases_dir = form.testcases_dir.data.strip()
            if testcases_dir:  # 仅当目录不为空时创建
                # 使用绝对路径更安全，避免相对路径问题
                abs_testcases_dir = os.path.abspath(testcases_dir)
                if not os.path.exists(abs_testcases_dir):
                    os.makedirs(abs_testcases_dir, exist_ok=True)
                    current_app.logger.info(f"创建测试用例目录: {abs_testcases_dir}")
            
            # 3. 创建Problem记录
            problem = Problem(
                title=form.title.data.strip(),
                description=form.description.data.strip(),
                difficulty=form.difficulty.data.strip(),
                output_type=form.output_type.data.strip(),
                initial_codes=initial_codes,
                function_signature=function_signature,
                testcases_dir=testcases_dir,
                template_file=form.template_file.data.strip()
            )
            db.session.add(problem)
            db.session.commit()  # 提交到数据库
            
            flash('题目创建成功！', 'success')
            return redirect(url_for('admin_manage_problems'))
            
        except json.JSONDecodeError as e:
            flash(f'JSON格式错误：{str(e)}', 'danger')
            db.session.rollback()  # 发生错误时回滚事务
        except IntegrityError as e:
            db.session.rollback()  # 回滚事务避免数据不一致
            if 'template_file' in str(e).lower():
                flash('英文标识已存在，请更换', 'danger')
            else:
                flash(f'数据库冲突：{str(e)}', 'danger')
        except OSError as e:
            flash(f'目录创建失败：{str(e)}', 'danger')
        except Exception as e:
            db.session.rollback()  # 全局异常回滚
            flash(f'系统错误：{str(e)}', 'danger')
            current_app.logger.error(f'创建题目失败：{str(e)}', exc_info=True)
    
    # 渲染模板：移除冗余的user变量（current_user在模板中可直接使用）
    return render_template(
        'admin/create_problem.html',
        user=current_user,
        form=form
    )


@app.route('/admin/problems/edit/<int:problem_id>', methods=['GET', 'POST'])
@login_required
def edit_problem(problem_id):
    """
    编辑题目：仅更新题目自身字段（title、description、difficulty、initial_codes、function_signature、testcases_dir、template_file）
    """
    if not current_user.is_superuser:
        abort(403)  # 权限校验
        
    # 1. 获取题目（404 校验）
    problem = Problem.query.get_or_404(problem_id)

    if request.method == 'POST':
        try:
            # 2. 解析表单数据（仅核心字段）
            form_data = {
                'title': request.form.get('title', '').strip(),
                'description': request.form.get('description', '').strip(),
                'difficulty': request.form.get('difficulty', '').strip(),
                'initial_codes': json.loads(request.form.get('initial_codes', '{}')),  # JSON 字段
                'function_signature': json.loads(request.form.get('function_signature', '{}')),  # JSON 字段
                'testcases_dir': request.form.get('testcases_dir', '').strip(),  # 测试用例目录
                'template_file': request.form.get('template_file', '').strip()   # 编译模板文件名
            }

            # 3. 校验必填项（可扩展 WTForms 验证，此处简单校验）
            if not form_data['title']:
                raise ValueError("题目名称不能为空")

            # 4. 更新题目字段
            problem.title = form_data['title']
            problem.description = form_data['description']
            problem.difficulty = form_data['difficulty']
            problem.initial_codes = form_data['initial_codes']
            problem.function_signature = form_data['function_signature']
            problem.testcases_dir = form_data['testcases_dir']
            problem.template_file = form_data['template_file']

            # 5. 提交数据库
            db.session.commit()
            flash('题目更新成功！', 'success')
            return redirect(url_for('admin_manage_problems'))

        except json.JSONDecodeError as e:
            db.session.rollback()
            flash(f'JSON 格式错误：{str(e)}', 'danger')
        except ValueError as e:
            db.session.rollback()
            flash(f'校验失败：{str(e)}', 'danger')
        except Exception as e:
            db.session.rollback()
            flash(f'系统错误：{str(e)}', 'danger')
            current_app.logger.error(f"编辑题目失败：{str(e)}", exc_info=True)

    # 6. GET 请求：渲染编辑表单（仅回显题目自身字段）
    return render_template(
        'admin/edit_problem.html', 
        user=current_user, 
        problem=problem
    )


@app.route('/admin/problems/delete/<int:problem_id>', methods=['POST'])  # 仅允许 POST
@login_required
def delete_problem(problem_id):
    # 查找题目
    problem = Problem.query.get_or_404(problem_id)
    
    try:
        # 删除题目（会自动删除关联的测试用例，因为设置了ondelete='CASCADE'）
        db.session.delete(problem)
        db.session.commit()
        
        # 记录成功消息
        flash(f'题目《{problem.title}》已成功删除', 'delete_success')
        
    except Exception as e:
        db.session.rollback()
        flash(f'删除失败：{str(e)}', 'delete_error')
    
    # 重定向回题目管理页面
    return redirect(url_for('admin_manage_problems'))

# 其他路由...（创建、编辑等）

if __name__ == '__main__':
    with app.app_context():
        db.create_all()  # 创建数据库表
    app.run(debug=True)

# ------------------------------
# 测试用例 & 模板管理（示例，需配套模板）
# ------------------------------

@app.route('/admin/problems/<int:problem_id>/testcases', methods=['GET', 'POST'])
@login_required
def manage_testcases(problem_id):
    """
    测试用例管理（修复示例交换逻辑 + 兼容删除操作）：
    - 1.in/1.out 固定为示例文件，标记新示例时自动交换内容
    - 修复 `form_testcases.index` 失效问题，改用 `original_idx` 定位
    """
    # 1. 权限 & 题目校验
    if not current_user.is_superuser:
        abort(403)
    problem = Problem.query.get_or_404(problem_id)
    
    # 2. 测试用例目录校验
    testcases_dir = problem.testcases_dir
    if not testcases_dir:
        flash('请先在「编辑题目」中设置测试用例目录', 'danger')
        return redirect(url_for('edit_problem', problem_id=problem_id))
    
    # 3. 拼接绝对路径
    project_root = os.path.dirname(current_app.root_path)
    testcases_path = os.path.join(project_root, testcases_dir)
    os.makedirs(testcases_path, exist_ok=True)

    # 4. GET：读取现有测试用例（1.in/1.out 为示例）
    testcases = []
    if request.method == 'GET':
        try:
            files = os.listdir(testcases_path)
            in_files = [f for f in files if f.lower().endswith('.in')]
            # 按编号排序（确保1.in在最前）
            in_files.sort(key=lambda x: int(x.split('.')[0]))
            
            for in_file in in_files:
                out_file = in_file.replace('.in', '.out')
                if out_file not in files:
                    continue
                
                # 读取文件内容
                with open(os.path.join(testcases_path, in_file), 'r') as f_in:
                    input_data = f_in.read().strip()
                with open(os.path.join(testcases_path, out_file), 'r') as f_out:
                    output_data = f_out.read().strip()
                
                # 标记示例用例（1.in对应的数据为示例）
                is_sample = in_file == '1.in'
                testcases.append({
                    'input': input_data,
                    'output': output_data,
                    'is_sample': is_sample
                })
        except Exception as e:
            current_app.logger.error(f"读取测试用例失败: {e}", exc_info=True)
            flash('读取测试用例失败，请检查目录权限', 'danger')
        
        return render_template(
            'admin/manage_testcases.html', 
            problem=problem,
            user=current_user, 
            testcases=testcases
        )
    
    # 5. POST：处理示例交换 + 兼容删除操作
    else:
        try:
            # 解析表单数据（含is_sample和原始索引original_idx）
            form_testcases = []
            max_idx = 0
            
            # 获取所有测试用例的最大索引
            for key in request.form.keys():
                if key.startswith('testcases[') and '][input]' in key:
                    try:
                        idx = int(key.split('[')[1].split(']')[0])
                        max_idx = max(max_idx, idx)
                    except (IndexError, ValueError):
                        continue
            
            # 提取测试用例数据（保留原始索引original_idx）
            for idx in range(max_idx + 1):
                input_val = request.form.get(f'testcases[{idx}][input]', '').strip()
                output_val = request.form.get(f'testcases[{idx}][output]', '').strip()
                is_sample = f'testcases[{idx}][is_sample]' in request.form
                
                if input_val or output_val:  # 跳过空用例（删除后的空行）
                    form_testcases.append({
                        'input': input_val,
                        'output': output_val,
                        'is_sample': is_sample,
                        'original_idx': idx  # 记录表单中的原始索引
                    })
            
            # 处理示例用例交换逻辑
            sample_candidates = [tc for tc in form_testcases if tc['is_sample']]
            if len(sample_candidates) > 1:
                flash('警告：仅第一个标记的用例会被设为示例', 'warning')
                target_sample = sample_candidates[0]
            elif len(sample_candidates) == 1:
                target_sample = sample_candidates[0]
            else:
                # 未标记时，默认第一个用例为示例（若存在）
                if not form_testcases:
                    flash('未添加测试用例，无需保存', 'info')
                    return redirect(url_for('manage_testcases', problem_id=problem_id))
                target_sample = form_testcases[0]
                target_sample['is_sample'] = True
            
            # 原始示例用例固定为列表第一个（对应1.in）
            original_sample_idx = 0  # 原始示例的索引（固定为0）
            original_sample = form_testcases[original_sample_idx]
            
            # 交换内容：目标示例 ↔ 原始示例（仅当两者不同时交换）
            if target_sample['original_idx'] != original_sample_idx:
                # 临时存储原始内容
                temp_input = original_sample['input']
                temp_output = original_sample['output']
                
                # 原始示例替换为目标内容
                original_sample['input'] = target_sample['input']
                original_sample['output'] = target_sample['output']
                original_sample['is_sample'] = True  # 确保原始示例标记为True
                
                # 目标示例替换为原始内容，并取消示例标记
                target_sample['input'] = temp_input
                target_sample['output'] = temp_output
                target_sample['is_sample'] = False
            
            # 清空原有测试用例文件
            for f in os.listdir(testcases_path):
                os.remove(os.path.join(testcases_path, f))
            
            # 写入新测试用例（1.in/1.out固定为示例，其他按编号存储）
            for i, tc in enumerate(form_testcases, 1):  # 编号从1开始
                in_filename = f"{i}.in"
                out_filename = f"{i}.out"
                
                # 若为原始示例（索引0），强制写入1.in/1.out
                if tc['original_idx'] == 0:
                    in_filename = '1.in'
                    out_filename = '1.out'
                
                with open(os.path.join(testcases_path, in_filename), 'w') as f_in:
                    f_in.write(tc['input'])
                with open(os.path.join(testcases_path, out_filename), 'w') as f_out:
                    f_out.write(tc['output'])
            
            flash('测试用例保存成功', 'success')
        
        except Exception as e:
            current_app.logger.error(f"保存测试用例失败: {e}", exc_info=True)
            flash(f'保存失败：{str(e)}', 'danger')
        
        return redirect(url_for('manage_testcases', problem_id=problem_id, user=current_user))


@app.route('/admin/problems/<int:problem_id>/template', methods=['GET', 'POST'])
@login_required
def edit_template(problem_id):
    """
    模板管理：读取并覆盖 `../code_templates/[template_file].cpp`
    - 自动处理编码、路径校验、日志调试
    """
    # 1. 权限与题目校验
    if not current_user.is_superuser:
        abort(403)
    problem = Problem.query.get_or_404(problem_id)
    
    # 2. 模板文件名验证（必须是 .cpp 文件）
    template_file = problem.template_file
    if not template_file or not template_file.endswith('.cpp'):
        flash('请先在「编辑题目」中设置有效的 C++ 模板文件名（.cpp 后缀）', 'danger')
        return redirect(url_for('edit_problem', problem_id=problem_id, user=current_user))
    
    # 3. 构建绝对路径（固定目录：项目根目录的上级/code_templates）
    try:
        # 安全路径处理：防止路径遍历攻击
        project_root = os.path.dirname(current_app.root_path)
        code_templates_dir = os.path.abspath(os.path.join(project_root, 'code_templates'))
        template_abs_path = os.path.abspath(os.path.join(code_templates_dir, template_file))
        
        # 校验路径是否在允许的目录内（安全加固）
        if not template_abs_path.startswith(code_templates_dir):
            raise ValueError("模板文件路径不允许超出 code_templates 目录")
        
        os.makedirs(code_templates_dir, exist_ok=True)  # 确保模板目录存在
    except Exception as e:
        current_app.logger.error(f"路径构建失败：{e}")
        flash(f'路径错误：{str(e)}', 'danger')
        return redirect(url_for('edit_problem', problem_id=problem_id, user=current_user))
    
    # 4. 传递路径到模板（用于前端显示）
    g.template_abs_path = template_abs_path  # 通过 g 对象传递
    
    # 5. GET：读取现有模板（带 UTF-8 编码和日志）
    if request.method == 'GET':
        templates = {'cpp': ''}
        current_app.logger.info(f"读取模板路径：{template_abs_path}")
        
        if os.path.exists(template_abs_path):
            try:
                with open(template_abs_path, 'r', encoding='utf-8') as f:
                    content = f.read()
                    templates['cpp'] = content
                    current_app.logger.info(f"读取成功（长度：{len(content)}字符）")  
            except UnicodeDecodeError:
                err_msg = "文件编码错误（需UTF-8格式）"
                current_app.logger.error(err_msg)
                flash(err_msg, 'danger')
            except Exception as e:
                current_app.logger.error(f"读取失败：{e}")
                flash(f'读取失败：{str(e)}', 'danger')
        else:
            current_app.logger.warning(f"文件不存在，将创建新文件：{template_abs_path}")
            flash(f'首次编辑，将创建新文件：<code>{template_abs_path}</code>', 'info')
        
        return render_template(
            'admin/edit_template.html',
            problem=problem,
            templates=templates,
            user=current_user
        )
    
    # 6. POST：覆盖保存模板
    else:
        try:
            new_content = request.form.get('templates[cpp]', '').strip()
            # 写入前再次校验路径（双重保险）
            if not template_abs_path.startswith(code_templates_dir):
                raise ValueError("禁止写入到模板目录外的文件")
            
            with open(template_abs_path, 'w', encoding='utf-8') as f:
                f.write(new_content)
            current_app.logger.info(f"模板保存成功（长度：{len(new_content)}字符）")
            flash('模板已覆盖保存！', 'success')
        except Exception as e:
            current_app.logger.error(f"模板保存失败：{e}，路径：{template_abs_path}")
            flash(f'保存失败（路径：<code>{template_abs_path}</code>）：{str(e)}', 'danger')
        
        return redirect(url_for('edit_template', problem_id=problem_id, user=current_user))

# 用户管理入口（占位）
@app.route('/admin/users')
@login_required  # 确保只有登录用户可访问
def admin_manage_users():
    # 1. 权限校验（仅 root 可访问）
    if current_user.username != 'root':
        flash('只有超级管理员可访问用户管理', 'danger')
        return redirect(url_for('problems'))
    
    # 2. 查询所有用户
    all_users = User.query.order_by(User.username.asc()).all()
    
    # 3. 生成 CSRF 令牌
    csrf_token_value = generate_csrf()
    
    # 4. 传递 current_user 到模板（关键！）
    return render_template(
        'admin/manage_users.html',
        users=all_users,
        csrf_token_value=csrf_token_value,
        user=current_user  # 新增：传递当前登录用户
    )

@app.route('/admin/user/toggle_delete', methods=['POST'])  # 仅允许POST请求
@login_required  # 必须登录
def toggle_delete():
    """切换用户注销状态（仅root可操作）"""
    # 1. 权限校验：仅root可访问
    if current_user.username != 'root':
        flash('只有超级管理员可操作', 'danger')
        return redirect(url_for('admin_manage_users'))
    
    # 2. 获取用户ID
    user_id = request.form.get('user_id')
    user = User.query.get_or_404(user_id)  # 自动处理不存在的用户
    
    # 3. 禁止操作root（可选）
    if user.username == 'root':
        flash('禁止操作超级管理员', 'danger')
        return redirect(url_for('admin_manage_users'))
    
    # 4. 切换注销状态 + 自动禁用账号
    user.is_deleted = not user.is_deleted
    if user.is_deleted:
        user.is_disabled = True  # 注销时自动禁用
    
    db.session.commit()
    flash(f"{user.username} 的注销状态已更新！", 'success')
    return redirect(url_for('admin_manage_users'))

@app.route('/admin/user/reset_password/<int:user_id>', methods=['GET', 'POST'])
@login_required
def reset_password(user_id):
    """
    管理员重置用户密码：
    - 仅 root 可访问（通过 @login_required + 用户名校验）
    - 显示目标用户的重置表单，右上角固定显示 root
    """
    # 1. 权限校验：仅 root 能操作
    if current_user.username != 'root':
        flash('只有超级管理员可重置密码', 'danger')
        return redirect(url_for('admin_manage_users'))
    
    # 2. 获取目标用户（自动处理404）
    user = User.query.get_or_404(user_id)
    
    # 3. POST 请求：处理密码重置
    if request.method == 'POST':
        new_password = request.form.get('new_password')
        if not new_password:
            flash('新密码不能为空', 'danger')
            return render_template('admin/reset_password.html', user=user, csrf_token_value=generate_csrf())
        
        # 假设 User 模型有 set_password 方法（需实现密码哈希）
        user.set_password(new_password)
        db.session.commit()
        flash('密码已成功重置！', 'success')
        return redirect(url_for('admin_manage_users'))
    
    # 4. GET 请求：渲染重置页面（传递目标用户和 CSRF 令牌）
    return render_template(
        'admin/reset_password.html',
        user=user,
        csrf_token_value=generate_csrf()
    )

@app.route('/admin/user/toggle_disable', methods=['POST'])
@login_required
def toggle_disable():
    """切换用户的账号禁用状态（仅 root 可操作）"""
    if current_user.username != 'root':
        return jsonify({"error": "权限不足"}), 403
    
    user_id = request.form.get('user_id')
    user = User.query.get_or_404(user_id)
    
    # 防止禁用 root 自身（可选，按需添加）
    if user.username == 'root':
        flash('禁止禁用超级管理员账号', 'danger')
        return redirect(url_for('admin_manage_users'))
    
    # 切换禁用状态
    user.is_disabled = not user.is_disabled
    db.session.commit()
    
    flash(f"{user.username} 的账号已{'禁用' if user.is_disabled else '启用'}", 'success')
    return redirect(url_for('admin_manage_users'))

# 新增：删除用户路由（物理删除）
@app.route('/admin/user/delete', methods=['POST'])
@login_required
def delete_user():
    """
    物理删除用户（彻底移除数据，仅root可操作）
    注意：此操作不可逆，建议先通过toggle_delete标记注销
    """
    # 1. 权限校验：仅root可执行
    if current_user.username != 'root':
        flash('只有超级管理员可删除用户', 'danger')
        return redirect(url_for('admin_manage_users'))
    
    # 2. 获取用户ID并校验
    user_id = request.form.get('user_id')
    if not user_id:
        flash('用户ID不能为空', 'danger')
        return redirect(url_for('admin_manage_users'))
    
    # 3. 查询用户并验证
    user = User.query.get_or_404(user_id)
    
    # 4. 禁止删除root自身
    if user.username == 'root':
        flash('禁止删除超级管理员账号', 'danger')
        return redirect(url_for('admin_manage_users'))
    
    # 5. 执行删除（不可逆操作）
    try:
        username = user.username  # 保存用户名用于提示
        db.session.delete(user)
        db.session.commit()
        flash(f'用户「{username}」已彻底删除', 'success')
    except Exception as e:
        db.session.rollback()
        flash(f'删除失败：{str(e)}', 'danger')
    
    return redirect(url_for('admin_manage_users'))

# 论坛管理入口（占位）
@app.route('/admin/forum')
@login_required
def admin_manage_forum():
    if current_user.username != 'root':
        flash('无权限访问', 'danger')
        return redirect(url_for('forum_index'))
    return render_template('admin/manage_forum.html', user=current_user)

# 新增：公开欢迎页（根路径 /）
@app.route('/')
def home():
    """公开欢迎页：未登录显示欢迎界面，已登录自动跳转至题库页"""
    # if current_user.is_authenticated:
    #     return redirect(url_for('problems'))  # 已登录 → 直接进题库
    return render_template('home.html', hide_nav=True)       # 未登录 → 显示欢迎页


# 改造：题库页（需登录，原/index功能迁移至此）
@app.route('/problems')
@login_required
def problems():
    if not current_user.is_authenticated:
        return redirect(url_for('login'))
    user = current_user
    problems = Problem.query.all()
    for problem in problems:
        problem.solved = problem in user.solved_problems
    return render_template('index.html', problems=problems, user=user)

# 解题页面路由
@app.route('/problem/<int:problem_id>/solve', methods=['GET', 'POST'])
def solve_problem(problem_id):
    problem = Problem.query.get_or_404(problem_id)
    return render_template('solve_problem.html', problem=problem)

# 题目详情页路由
@app.route('/problem/<int:problem_id>')
def problem_detail(problem_id):
    if not current_user.is_authenticated:
        return redirect(url_for('login'))
    problem = Problem.query.get_or_404(problem_id)
    user = current_user
    discussion_count = Discussion.query.filter_by(problem_id=problem_id).count()
    sample_case = problem.get_sample_test_case()
    is_favorited = Favorite.query.filter_by(user_id=user.id, problem_id=problem_id).first() is not None
    return render_template(
        'problem_detail.html',
        problem=problem,
        user=user,
        discussion_count=discussion_count,
        initial_codes=problem.initial_codes,
        function_signatures=problem.function_signature,
        sample_case=sample_case,
        is_favorited=is_favorited
    )

# 每日打卡路由
@app.route('/clock_in', methods=['GET', 'POST'])
def clock_in():
    from datetime import datetime, date
    import calendar
    from .models import ClockInRecord

    if not current_user.is_authenticated:
        flash('请先登录', 'danger')
        return redirect(url_for('login'))
    
    user = current_user
    today = date.today()
    target_year = request.args.get('year', type=int, default=today.year)
    target_month = request.args.get('month', type=int, default=today.month)

    if target_month < 1 or target_month > 12:
        flash('无效月份，已自动调整为当前月份', 'warning')
        return redirect(url_for('clock_in', year=today.year, month=today.month))

    if request.method == 'POST':
        clock_date_str = request.form.get('clock_date', '')
        try:
            clock_date = datetime.strptime(clock_date_str, "%Y-%m-%d").date()
            if clock_date != today:
                flash('仅可打卡当天日期', 'warning')
                return redirect(url_for('clock_in', year=target_year, month=target_month))
            
            existing = ClockInRecord.query.filter_by(
                user_id=user.id, date=clock_date
            ).first()
            if existing:
                flash('今日已打卡', 'warning')
            else:
                new_record = ClockInRecord(user_id=user.id, date=clock_date)
                db.session.add(new_record)
                db.session.commit()
                flash('打卡成功！', 'success')
        except ValueError as e:
            flash(f'日期格式错误：{str(e)}', 'danger')
        except Exception as e:
            db.session.rollback()
            flash(f'系统错误：{str(e)}', 'danger')
        return redirect(url_for('clock_in', year=target_year, month=target_month))

    first_day = datetime(target_year, target_month, 1)
    current_month_name = first_day.strftime('%B')
    month_start_weekday = first_day.isoweekday() % 7
    num_days = calendar.monthrange(target_year, target_month)[1]
    month_days = range(1, num_days + 1)

    start_date = date(target_year, target_month, 1)
    end_date = date(target_year, target_month, num_days)
    clocked_records = ClockInRecord.query.filter(
        ClockInRecord.user_id == user.id,
        ClockInRecord.date.between(start_date, end_date)
    ).all()
    clocked_count = len(clocked_records)
    clocked_dates = [r.date.strftime("%Y-%m-%d") for r in clocked_records]

    prev_year, prev_month = target_year, target_month - 1
    if prev_month < 1:
        prev_year -= 1
        prev_month = 12
    next_year, next_month = target_year, target_month + 1
    if next_month > 12:
        next_year += 1
        next_month = 1

    return render_template('clock_in.html',
                           user=user,
                           current_year=target_year,
                           current_month=current_month_name,
                           current_month_num=target_month,
                           month_days=month_days,
                           month_start_weekday=month_start_weekday,
                           today_day=today.day,
                           today_month=today.month,
                           today_year=today.year,
                           clocked_dates=clocked_dates,
                           clocked_count=clocked_count,
                           prev_year=prev_year,
                           prev_month=prev_month,
                           next_year=next_year,
                           next_month=next_month)

# 获取有效题目ID接口
@app.route('/api/problems/valid_ids')
def get_valid_problem_ids():
    valid_ids = [p.id for p in Problem.query.order_by(Problem.id).all()]
    return jsonify({"valid_ids": valid_ids})

# 获取下一题ID接口
@app.route('/api/problems/<int:current_id>/next')
def get_next_problem(current_id):
    next_problem = Problem.query.filter(Problem.id > current_id).order_by(Problem.id).first()
    return jsonify({"next_id": next_problem.id}) if next_problem else jsonify({"next_id": None}), 404

# 切换收藏状态接口
@app.route('/toggle_favorite/<int:problem_id>', methods=['POST'])
@csrf.exempt
def toggle_favorite(problem_id):
    if not current_user.is_authenticated:
        return jsonify({"error": "未登录", "status": "failed"}), 401
    user = current_user
    problem = Problem.query.get_or_404(problem_id)
    favorite = Favorite.query.filter_by(user_id=user.id, problem_id=problem_id).first()
    try:
        if favorite:
            db.session.delete(favorite)
            db.session.commit()
            return jsonify({"status": "success", "action": "unfavorite", "is_favorited": False})
        else:
            new_favorite = Favorite(user_id=user.id, problem_id=problem_id)
            db.session.add(new_favorite)
            db.session.commit()
            return jsonify({"status": "success", "action": "favorite", "is_favorited": True})
    except Exception as e:
        db.session.rollback()
        return jsonify({"status": "error", "message": f"操作失败: {str(e)}"}), 500

# 收藏题目路由
@app.route('/favorite/<int:problem_id>', methods=['POST'])
def favorite_problem(problem_id):
    if not current_user.is_authenticated:
        flash('请先登录', 'danger')
        return redirect(url_for('login'))
    user = current_user
    problem = Problem.query.get_or_404(problem_id)
    existing = Favorite.query.filter_by(user_id=user.id, problem_id=problem.id).first()
    if existing:
        flash('该题目已在收藏夹中', 'info')
    else:
        new_favorite = Favorite(user_id=user.id, problem_id=problem.id)
        db.session.add(new_favorite)
        db.session.commit()
        flash('收藏成功', 'success')
    return redirect(url_for('problem_detail', problem_id=problem_id))

# 取消收藏路由
@app.route('/unfavorite/<int:problem_id>', methods=['POST'])
def unfavorite_problem(problem_id):
    if not current_user.is_authenticated:
        return jsonify({"status": "failed", "message": "请先登录"}), 401
    user = current_user
    favorite = Favorite.query.filter_by(user_id=user.id, problem_id=problem_id).first()
    if favorite:
        db.session.delete(favorite)
        db.session.commit()
        return jsonify({"status": "success", "message": "已取消收藏", "is_favorited": False})
    else:
        return jsonify({"status": "failed", "message": "该题目不在收藏夹中"}), 400

# 我的收藏页路由
@app.route('/my_favorites')
def my_favorites():
    if not current_user.is_authenticated:
        return redirect(url_for('login'))
    user = current_user
    favorites = Favorite.query.filter_by(user_id=user.id).order_by(Favorite.created_at.desc()).all()
    return render_template('my_favorites.html', favorites=favorites, user=user)

# 排行榜路由
@app.route('/rank')
def rank():
    # 1. 数据库查询：关联已解决问题 + 过滤游客 + 分组排序
    csrf_token = generate_csrf()  # 生成令牌
    rank_data = db.session.query(
        User,  # 查询用户对象
        func.count(Problem.id).label('ac_count')  # 统计已解决题目数，别名 ac_count
    ).outerjoin(
        User.solved_problems  # 关联用户的已解决问题（需确保 User 模型定义了 solved_problems 关系）
    ).filter(
        # 新增：同时排除 guest 和 root
        User.username.notin_(['guest', 'root'])  
    ).group_by(
        User.id  # 按用户 ID 分组（避免重复统计）
    ).order_by(
        func.count(Problem.id).desc(),  # 优先按解题数降序
        User.username.asc()  # 解题数相同时，按用户名升序
    ).all()
    
    # 2. 渲染模板（保留原有参数）
    return render_template(
        'rank.html',
        rank_data=rank_data,
        user=current_user
    )

# 审核通过（仅 root 可操作）
@app.route('/forum/approve/<int:post_id>', methods=['POST'])
@login_required
def approve_post(post_id):
    if current_user.username != 'root':
        flash('仅超级管理员可审核帖子', 'danger')
        return redirect(url_for('forum_index'))
    
    post = ForumPost.query.get_or_404(post_id)
    if post.is_approved:
        flash('该帖子已通过审核', 'warning')
        return redirect(url_for('forum_audit'))
    
    # 标记为“通过”
    post.is_approved = True
    db.session.commit()
    
    # 发送通知：明确提示“已通过，可查看”
    notify_content = f'你的帖子《{post.title}》已通过审核，现在可以在论坛中查看。'
    new_notification = Notification(content=notify_content, recipient_id=post.user_id)
    db.session.add(new_notification)
    db.session.commit()
    
    flash(f'帖子「{post.title}」已通过审核', 'success')
    return redirect(url_for('forum_audit'))


# 审核驳回（删除帖子 + 发通知）
@app.route('/forum/reject/<int:post_id>', methods=['POST'])
@login_required
def reject_post(post_id):
    if current_user.username != 'root':
        flash('仅超级管理员可审核帖子', 'danger')
        return redirect(url_for('forum_index'))
    
    post = ForumPost.query.get_or_404(post_id)  # 获取帖子
    
    # 1. 删除帖子（从数据库移除）
    db.session.delete(post)
    db.session.commit()
    
    # 2. 发送驳回通知给作者
    notify_content = f'你的帖子《{post.title}》未通过审核，请修改后重新提交。'
    new_notification = Notification(
        content=notify_content, 
        recipient_id=post.user_id  # 帖子作者ID
    )
    db.session.add(new_notification)
    db.session.commit()
    
    flash(f'帖子「{post.title}」已驳回', 'success')
    return redirect(url_for('forum_audit'))  # 回到审核列表


# 原有审核页面路由：保持不变
@app.route('/forum/forum_audit')
@login_required
def forum_audit():
    """论坛审核页面：仅 root 可见，展示所有待审核帖子"""
    if current_user.username != 'root':
        flash('仅超级管理员可进入审核系统', 'danger')
        return redirect(url_for('forum_index'))
    
    # 查询所有待审核的帖子（is_approved=False）
    pending_posts = ForumPost.query.filter_by(is_approved=False).order_by(ForumPost.created_at.desc()).all()
    
    return render_template('forum/forum_audit.html', pending_posts=pending_posts, user=current_user)


# 通知列表页面
@app.route('/notifications')
@login_required
def notifications():
    # 使用Notification模型查询
    user_notifications = Notification.query.filter_by(
        recipient_id=current_user.id
    ).order_by(Notification.created_at.desc()).all()
    return render_template('notifications.html', notes=user_notifications, user=current_user)

# 获取未读通知数量
@app.route('/notifications/unread/count')
@login_required  # 必须登录才能获取
def unread_notify_count():
    # 严格筛选：当前用户 + 未读
    unread_count = Notification.query.filter_by(
        recipient_id=current_user.id,  
        is_read=False  
    ).count()
    return jsonify({'count': unread_count})

# 标记通知为已读
@app.route('/notifications/read/<int:notify_id>', methods=['POST'])
@login_required
def mark_notify_read(notify_id):
    notification = Notification.query.get_or_404(notify_id)
    if notification.recipient_id != current_user.id:
        return jsonify({'error': '无权操作'}), 403
    notification.is_read = True
    db.session.commit()
    return jsonify({'success': True})

@app.route('/forum/toggle_pin/<int:post_id>', methods=['POST'])
@login_required
def toggle_post_pin(post_id):
    """切换帖子置顶状态（仅 root 可操作）"""
    # 权限校验：仅超级管理员(root)能操作
    if current_user.username != 'root':
        flash('仅超级管理员可设置帖子置顶', 'danger')
        return redirect(url_for('forum_index'))
    
    # 查询帖子
    post = ForumPost.query.get_or_404(post_id)
    
    # 切换置顶状态
    post.is_pinned = not post.is_pinned
    db.session.commit()
    
    # 反馈提示
    flash(f'帖子「{post.title}」已{"置顶" if post.is_pinned else "取消置顶"}', 'success')
    return redirect(url_for('forum_index'))

@app.route('/user/posts')
@login_required  # 仅登录用户可访问
def user_posts():
    page = request.args.get('page', 1, type=int)
    # 查询当前用户的所有帖子（按发布时间倒序）
    posts = ForumPost.query.filter_by(user_id=current_user.id)\
                           .order_by(ForumPost.created_at.desc())\
                           .paginate(page=page, per_page=10)  # 每页10条
    return render_template('user_posts.html', posts=posts, user=current_user)

# 论坛首页
@app.route('/forum')
def forum_index():
    page = request.args.get('page', 1, type=int)
    
    if current_user.is_authenticated and current_user.username == 'root':
        # root逻辑：自己的所有帖子 + 他人的所有帖子（含未审核）
        own_posts = ForumPost.query.filter_by(user_id=current_user.id)
        other_posts = ForumPost.query.filter(ForumPost.user_id != current_user.id)
        posts_query = own_posts.union(other_posts)
    else:
        # 普通用户逻辑：
        if current_user.is_authenticated:
            # ① 自己的所有帖子（含待审核）
            own_posts = ForumPost.query.filter_by(user_id=current_user.id)
            # ② 他人的帖子：仅已审核 或 已置顶
            other_posts = ForumPost.query.filter(
                ForumPost.user_id != current_user.id,
                or_(ForumPost.is_approved == True, ForumPost.is_pinned == True)
            )
            # 合并：自己的所有帖子 + 他人的合格帖子
            posts_query = own_posts.union(other_posts)
        else:
            # 游客：仅见他人已审核/置顶帖
            posts_query = ForumPost.query.filter(
                or_(ForumPost.is_approved == True, ForumPost.is_pinned == True)
            )
    
    # 排序规则：置顶 > 自己的帖子 > 时间倒序
    posts = posts_query.order_by(
        desc(ForumPost.is_pinned),
        desc(ForumPost.user_id == current_user.id if current_user.is_authenticated else False),
        desc(ForumPost.created_at)
    ).paginate(page=page, per_page=9)
    
    return render_template('forum/index.html', posts=posts, user=current_user)



# 帖子详情页
@app.route('/forum/post/<int:post_id>', methods=['GET', 'POST'])
def forum_post(post_id):
    post = ForumPost.query.get_or_404(post_id)
    csrf_token_value = generate_csrf()  # 使用非冲突变量名
    
    if request.method == 'POST':
        if not current_user.is_authenticated:
            flash('请先登录才能发表评论！')
            return redirect(url_for('login', next=request.url))
            
        if 'csrf_token' not in request.form:
            flash('评论失败：CSRF令牌缺失', 'danger')
            return render_template('forum/post.html', post=post, user=current_user, csrf_token_value=csrf_token_value)
        
        content = request.form.get('content')
        if content:
            comment = ForumComment(
                content=content,
                post_id=post.id,
                user_id=current_user.id
            )
            db.session.add(comment)
            db.session.commit()
            flash('评论成功！')
            return redirect(url_for('forum_post', post_id=post.id))
    
    return render_template('forum/post.html', post=post, user=current_user, csrf_token_value=csrf_token_value)

# 发布新帖子
@app.route('/forum/new', methods=['GET', 'POST'])
@login_required
def new_post():
    from datetime import datetime  
    from .models import ForumPost, Notification, User  # 🔴 导入Notification和User
    csrf_token_value = generate_csrf()  # 生成CSRF令牌
    
    if request.method == 'POST':
        print("POST数据:", dict(request.form))  
        print("Cookie:", dict(request.cookies))
        
        # 校验CSRF令牌
        if 'csrf_token' not in request.form:
            flash('发布失败：CSRF令牌缺失，请刷新页面', 'danger')
            return render_template('forum/forum/new_post.html', user=current_user, csrf_token_value=csrf_token_value)
        
        # 获取表单数据
        title = request.form.get('title', '').strip()
        content = request.form.get('content', '').strip()
        
        # 校验空值
        if not title or not content:
            flash('标题和内容不能为空', 'danger')
            return render_template('forum/new_post.html', user=current_user, csrf_token_value=csrf_token_value)
        
        try:
            # 自动判断审核状态：root 发帖直接通过，否则待审核
            is_approved = current_user.username == 'root'  
            
            # 创建帖子
            post = ForumPost(
                title=title,
                content=content,
                user_id=current_user.id,
                created_at=datetime.utcnow(),
                is_approved=is_approved  
            )
            db.session.add(post)
            db.session.commit()  # 提交帖子到数据库
            
            # 🔴 新增：普通用户发帖时，通知root审核
            if not is_approved:  # 仅普通用户发帖时触发
                root = User.query.filter_by(username='root').first()
                if root:
                    # 构造通知内容
                    notify_content = f'用户 {current_user.username} 发布了新帖子《{title}》，需审核。'
                    # 创建通知
                    notification = Notification(
                        content=notify_content,
                        recipient_id=root.id,  # root的ID
                        is_read=False  # 标记为未读
                    )
                    db.session.add(notification)
                    db.session.commit()  # 提交通知到数据库
            
            flash('帖子发布成功！', 'success')
            return redirect(url_for('forum_post', post_id=post.id))
        
        except Exception as e:
            db.session.rollback()  # 异常时回滚
            flash(f'发布失败：{str(e)}', 'danger')
            return render_template('forum/new_post.html', user=current_user, csrf_token_value=csrf_token_value)
    
    # GET请求：渲染发帖页面
    return render_template('forum/new_post.html', user=current_user, csrf_token_value=csrf_token_value)

# 编辑帖子
@app.route('/forum/post/<int:post_id>/edit', methods=['GET', 'POST'])
@login_required
def edit_post(post_id):
    post = ForumPost.query.get_or_404(post_id)
    
    # 权限判断：允许 root 或原作者编辑
    # 核心修改：如果是 root，直接通过；否则检查是否为原作者
    if current_user.username != 'root' and current_user.id != post.user_id:
        flash('你没有权限编辑这篇帖子！')
        return redirect(url_for('forum_post', post_id=post.id))
    
    csrf_token_value = generate_csrf()  # 使用非冲突变量名
    
    if request.method == 'POST':
        if 'csrf_token' not in request.form:
            flash('编辑失败：CSRF令牌缺失', 'danger')
            return render_template('forum/edit_post.html', post=post, user=current_user, csrf_token_value=csrf_token_value)
        
        title = request.form.get('title')
        content = request.form.get('content')
        
        if not title or not content:
            flash('标题和内容不能为空！')
            return render_template('forum/edit_post.html', post=post, user=current_user, csrf_token_value=csrf_token_value)
        
        post.title = title
        post.content = content
        post.updated_at = datetime.utcnow()
        db.session.commit()
        flash('帖子更新成功！')
        return redirect(url_for('forum_post', post_id=post.id))
        
    return render_template('forum/edit_post.html', post=post, user=current_user, csrf_token_value=csrf_token_value)

# 删除帖子
@app.route('/forum/post/<int:post_id>/delete', methods=['POST'])
@login_required
def delete_post(post_id):
    post = ForumPost.query.get_or_404(post_id)
    
    # 权限判断：允许 root 或原作者删除（核心修改）
    if current_user.username != 'root' and current_user.id != post.user_id:
        flash('你没有权限删除这篇帖子！')
        return redirect(url_for('forum_post', post_id=post.id))
    
    # 检查CSRF令牌（保留原有逻辑）
    if 'csrf_token' not in request.form:
        flash('删除失败：CSRF令牌缺失', 'danger')
        return redirect(url_for('forum_post', post_id=post.id))
    
    # 执行删除操作
    db.session.delete(post)
    db.session.commit()
    flash('帖子已删除！')
    return redirect(url_for('forum_index'))

# 删除评论
@app.route('/forum/comment/<int:comment_id>/delete', methods=['POST'])
@login_required
def delete_comment(comment_id):
    comment = ForumComment.query.get_or_404(comment_id)
    post_id = comment.post_id
    
    if current_user.id != comment.user_id:
        flash('你没有权限删除这条评论！')
        return redirect(url_for('forum_post', post_id=post_id))
    
    if 'csrf_token' not in request.form:
        flash('删除失败：CSRF令牌缺失', 'danger')
        return redirect(url_for('forum_post', post_id=post_id))
    
    db.session.delete(comment)
    db.session.commit()
    flash('评论已删除！')
    return redirect(url_for('forum_post', post_id=post_id))

# 点赞/取消点赞接口
@app.route('/forum/post/<int:post_id>/like', methods=['POST'])
@login_required
def like_post(post_id):
    try:
        # 检查帖子是否存在
        post = ForumPost.query.get_or_404(post_id)
        
        # 检查用户是否已点赞
        existing_like = PostLike.query.filter_by(
            user_id=current_user.id,
            post_id=post_id
        ).first()
        
        if existing_like:
            # 已点赞：取消点赞
            db.session.delete(existing_like)
            action = "unlike"
        else:
            # 未点赞：添加点赞
            new_like = PostLike(user_id=current_user.id, post_id=post_id)
            db.session.add(new_like)
            action = "like"
        
        db.session.commit()
        # 返回最新点赞数和操作结果
        like_count = PostLike.query.filter_by(post_id=post_id).count()
        return jsonify({
            "status": "success",
            "action": action,
            "like_count": like_count,
            "is_liked": action == "like"
        })
    
    except Exception as e:
        db.session.rollback()
        return jsonify({
            "status": "error",
            "message": f"操作失败：{str(e)}"
        }), 500


# 提交代码接口
@app.route('/submit/<int:problem_id>', methods=['POST'])
def submit(problem_id):
    if not current_user.is_authenticated:
        return jsonify({"error": "未登录"}), 401
    problem = Problem.query.get_or_404(problem_id)
    user = current_user
    
    # 接收并还原代码
    raw_code = request.form.get('code', '').strip()
    code = html.unescape(raw_code)
    code = html.unescape(code)
    language = request.form.get('language', 'python')
    if not code:
        return jsonify({"error": "代码不能为空"}), 400
    
    # 评测逻辑
    result = judge.judge_submission(code, language, problem)
    
    # 时间处理
    now = datetime.now(tz)
    today = now.date()
    today_start = datetime.combine(today, datetime.min.time()).replace(tzinfo=tz)
    today_end = datetime.combine(today, datetime.max.time()).replace(tzinfo=tz)
    
    # 今日AC统计
    previous_acs = Submission.query.filter(
        Submission.user_id == user.id,
        Submission.status == "accepted",
        Submission.timestamp >= today_start,
        Submission.timestamp < today_end
    ).all()
    previous_ac_count = len(previous_acs)
    
    # 处理full_code转义
    raw_full_code = result.get('full_code', '')
    full_code = html.unescape(raw_full_code)
    # 创建提交记录
    submission = Submission(
        user_id=user.id,
        problem_id=problem.id,
        code=code,
        full_code=full_code,
        language=language,
        status=result['status'],
        execution_time=result['execution_time'],
        timestamp=now
    )
    
    # 自动打卡逻辑
    auto_clocked = False
    if result['status'] == "accepted" and previous_ac_count == 0:
        existing_clock = ClockInRecord.query.filter_by(
            user_id=user.id, 
            date=today
        ).first()
        if not existing_clock:
            clock_in_record = ClockInRecord(user_id=user.id, date=today)
            db.session.add(clock_in_record)
            auto_clocked = True
            print(f"✓ 自动打卡成功：用户 {user.username} 于 {today} 完成首题AC")
        else:
            print(f"✗ 未触发打卡：用户 {user.username} 今日已打卡")
    elif result['status'] == "accepted":
        print(f"✗ 未触发打卡：用户 {user.username} 今日已有 {previous_ac_count} 个AC提交")
    
    # 保存提交记录
    db.session.add(submission)
    if result['status'] == "accepted" and problem not in user.solved_problems:
        user.solved_problems.append(problem)
    db.session.commit()
    
    # 返回结果
    return jsonify({
        "status": result['status'],
        "execution_time": result['execution_time'],
        "results": result['results'],
        "submission_id": submission.id,
        "error": result.get('error', ''),
        "auto_clocked": auto_clocked
    })

# 提交记录页路由
@app.route('/submissions')
def submissions():
    if not current_user.is_authenticated:
        return redirect(url_for('login'))
    user = current_user
    submissions = Submission.query.filter_by(user_id=user.id).order_by(Submission.timestamp.desc()).all()
    for submission in submissions:
        submission.problem_title = Problem.query.get(submission.problem_id).title
    return render_template('submissions.html', submissions=submissions, user=user)

# 提交详情页路由
@app.route('/submission/<int:submission_id>')
def submission_detail(submission_id):
    if not current_user.is_authenticated:
        return redirect(url_for('login'))
    submission = Submission.query.get_or_404(submission_id)
    if submission.user_id != current_user.id:
        return redirect(url_for('index'))
    problem = Problem.query.get(submission.problem_id)
    user = current_user
    return render_template('submission_detail.html', submission=submission, problem=problem, user=user)

# 题目讨论页路由
@app.route('/problem/<int:problem_id>/discussions')
def problem_discussions(problem_id):
    if not current_user.is_authenticated:
        return redirect(url_for('login'))
    problem = Problem.query.get_or_404(problem_id)
    discussions = Discussion.query.filter_by(problem_id=problem_id).order_by(Discussion.timestamp.desc()).all()
    user = current_user
    return render_template('discussions.html', problem=problem, discussions=discussions, user=user)

@app.route('/guest-login')
def guest_login():
    try:
        # 查找或创建游客用户
        guest_user = User.query.filter_by(username='guest').first()
        
        if not guest_user:
            # 创建新游客（确保字段与你的User模型匹配）
            guest_user = User(
                username='guest',
                email=f'guest_{uuid.uuid4()}@example.com',
                # 若模型密码字段非必填，可省略；若必填，可设为空字符串
                password_hash=''  # 或根据你的模型调整字段名，如hashed_pwd
            )
            db.session.add(guest_user)
            db.session.commit()
        
        # 登录游客用户（Flask-Login核心方法）
        login_user(guest_user, remember=False)  # 不记住游客登录状态
        flash('', 'info')
        return redirect(url_for('problems'))  # 强制跳转到主页
    
    except Exception as e:
        # 捕获异常并提示
        db.session.rollback()  # 回滚数据库操作
        flash(f'登录失败：{str(e)}', 'danger')
        return redirect(url_for('login'))  # 失败时返回登录页

@app.route('/login', methods=['GET', 'POST'])
def login():
    login_error = None
    
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        user = User.query.filter_by(username=username).first()
        
        if user:
            # 先检查账号状态
            if user.is_deleted:
                login_error = '该账号已注销'
            elif user.is_disabled:
                login_error = '该账号已被禁用'
            # 再验证密码
            elif check_password_hash(user.password_hash, password):
                login_user(user)
                return redirect(url_for('admin_dashboard') if user.username == 'root' else url_for('problems'))
            else:
                login_error = '用户名或密码错误'
        else:
            login_error = '用户名或密码错误'
        
        return render_template('login.html', login_error=login_error, hide_nav=True)
    
    return render_template('login.html', login_error=login_error, hide_nav=True)

@app.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        confirm_password = request.form.get('confirm_password')  # 获取确认密码字段
        
        # 新增：验证密码一致性
        if password != confirm_password:
            flash('两次输入的密码不一致', 'danger')
            return render_template('login.html', hide_nav=True)
        
        # 检查用户名是否已存在
        if User.query.filter_by(username=username).first():
            flash('用户名已存在', 'danger')
            return render_template('login.html', hide_nav=True)
        
        # 创建用户并加密密码（修复函数名错误）
        user = User(
            username=username,
            password_hash=generate_password_hash(password)  # 修正为正确的函数名
        )
        
        db.session.add(user)
        db.session.commit()
        
        # 注册后自动登录
        login_user(user)
        return redirect(url_for('problems'))  # 注册后跳转至题库页
    
    # GET请求时传递hide_nav=True，隐藏导航栏
    return render_template('register.html', hide_nav=True)
    
# 退出登录路由
@app.route('/logout')
def logout():
    logout_user()  # 使用Flask-Login的logout_user方法
    flash('已成功退出登录', 'info')
    return redirect(url_for('login'))

# 初始化数据库
with app.app_context():
    db.create_all()
    create_default_superuser()  # 自动创建超级管理员

# 启动应用
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)